using System;
using NUnit.Framework;
using dnAnalytics.LinearAlgebra;
using dnAnalytics.Statistics.Distributions;

namespace dnAnalytics.Tests.Statistics.Distributions
{
    [TestFixture]
    public class VectorNormalTests
    {
        private const double mAcceptableError = 1e-12;

        //[Test]
        //[ExpectedException(typeof(ArgumentOutOfRangeException))]
        //public void NormalConstructorFail()
        //{
        //    Matrix cov = new DenseMatrix(new double[,] { { 1.0, 1.0 }, { -1.0, 2.0 } });
        //    Vector mean = new DenseVector(new double[] { 5.0, 5.0 });

        //    // Build a new vector normal distribution.
        //    VectorNormal normal = new VectorNormal(mean, cov);
        //}

        [Test]
        public void StandardNormal()
        {
            VectorNormal normal = new VectorNormal(5);

            // Test the mean.
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(0.0, normal.Mean[i]);
            }

            // Test the covariance.
            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    if(i == j)
                    {
                        Assert.AreEqual(1.0, normal.Covariance[i,j]);
                    }
                    else
                    {
                        Assert.AreEqual(0.0, normal.Covariance[i,j]);
                    }
                }
            }

            // Test the pdf.
            Assert.AreEqual(0.010105326013812, normal.Density(new DenseVector(5, 0.0)), mAcceptableError);
            Assert.AreEqual(8.294956719377678e-004, normal.Density(new DenseVector(5, 1.0)), mAcceptableError);

            // Test the mode.
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(0.0, normal.Mode[i]);
            }

            // Test the median.
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(0.0, normal.Median[i]);
            }

            // Test the entropy.
            Assert.AreEqual(7.094692666023364, normal.Entropy, mAcceptableError);
        }

        [Test]
        public void NormalFromCovariance()
        {
            Matrix cov = new DenseMatrix(new double[,] {{1.0,0.9}, {0.9, 1.0}});
            Vector mean = new DenseVector(new double[] { 5.0, 5.0 });

            // Check that these are valid mean and covariances.
            Assert.DoesNotThrow(() => VectorNormal.CheckParameters(mean, cov));

            // Build a new vector normal distribution.
            VectorNormal normal = new VectorNormal(mean, cov);

            // Test the mean.
            Assert.AreEqual(5.0, normal.Mean[0]);
            Assert.AreEqual(5.0, normal.Mean[1]);

            // Test the covariance.
            Assert.AreEqual(1.0, normal.Covariance[0, 0]);
            Assert.AreEqual(0.9, normal.Covariance[0, 1]);
            Assert.AreEqual(0.9, normal.Covariance[1, 0]);
            Assert.AreEqual(1.0, normal.Covariance[1, 1]);

            // Test the mode.
            Assert.AreEqual(5.0, normal.Mode[0]);
            Assert.AreEqual(5.0, normal.Mode[1]);

            // Test the median.
            Assert.AreEqual(5.0, normal.Median[0]);
            Assert.AreEqual(5.0, normal.Median[1]);

            // Test the entropy.
            Assert.AreEqual(2.007511462998520, normal.Entropy, mAcceptableError);

            // Get the RNG.
            System.Random rnd = normal.RandomNumberGenerator;
        }

        [Test]
        public void GetSetRNG()
        {
            VectorNormal normal = new VectorNormal(5);

            // Try getting the random number generator.
            System.Random rnd = normal.RandomNumberGenerator;
            // Try setting the random number generator.
            normal.RandomNumberGenerator = new System.Random();
        }
    }
}